#include "libtabla.h"

#define RET_ERROR -1
#define RET_OK 0

void _lockTabla(Tabla*);
void _unlockTabla(Tabla* );
int _existeArchivo(const char * filename);
int _getElementosActuales(Tabla * tabla);
int _getNewPid(Tabla * tabla);

int crearTabla(Tabla* tabla, int cantMaxElementos){
	int shmem, i;
	char* shname = (char*)malloc(20);
	 
	/*Creo el espacio de memoria compartida con la maxima cantidad posible de elementos*/	
	shmem = shmget(IPC_PRIVATE, sizeof(PCB)*cantMaxElementos, IPC_CREAT | 0666);
	if(shmem == -1){
		return RET_ERROR;
	}
	//Apunto _data a la memoria compartida que contiene los elementos propios de la tabla
	tabla->_data = (PCB *)shmat(shmem, NULL, 0);
	if(tabla->_data == NULL){
		perror("No se pudo crear la tabla");
		return RET_ERROR;
	}

	//Creo un nombre de semaforo en base a la posición de memoria compartida para usar como identificador
	sprintf(shname, "sem%i", shmem);
	
	//Guardo la maxima cantidad de elementos de la tabla, para poder recorrerla luego
	tabla->_cantMaxElementos = cantMaxElementos;	
	tabla->_shmem = shmem;
	tabla->_sem_name = shname;
	
	//Creo el semaforo que va acontrolar a la tabla en base al nombre de semaforo que cree
	tabla->_mutex = sem_open(shname,O_CREAT,0644,1);
  	if(tabla->_mutex == SEM_FAILED)
    {
      perror("No se pudo crear el semaforo");
      sem_unlink(shname);
      return RET_ERROR;
    }

	//Lockeo la tabla y la inicializo en 0
	_lockTabla(tabla);
		for(i = 0; i < cantMaxElementos; i++){
			tabla->_data[i].pid = 0;
			tabla->_data[i].state = 0;
			tabla->_data[i].inMemory = 0;
			tabla->_data[i].proc = NULL;
		}
	_unlockTabla(tabla);

	return shmem;
}

int abrirTabla(Tabla* tabla, int cantMaxElementos, int shmem){
	char* shname = (char*)malloc(20);
	
	//Guardo la posición de shmem
	tabla->_shmem = shmem;
	//Apunto _data a la memoria compartida que contiene los elementos propios de la tabla
	tabla->_data = (PCB *)shmat(shmem, NULL, 0);
	if(tabla->_data == NULL){
		return RET_ERROR;
	}

	//Creo un nombre de semaforo en base a la posición de memoria compartida para usar como identificador
	sprintf(shname, "sem%i", shmem);
		
	//Guardo la maxima cantidad de elementos de la tabla, para poder recorrerla luego
	tabla->_cantMaxElementos = cantMaxElementos;
	tabla->_sem_name = shname;	
	
	//Creo el semaforo que va acontrolar a la tabla en base al nombre de semaforo que cree
	tabla->_mutex = sem_open(shname,0,0644,0);
  	if(tabla->_mutex == SEM_FAILED)
	{
		perror("No se pudo abrir el semaforo");
		sem_close(tabla->_mutex);
		return RET_ERROR;
    	}

	return RET_OK;
}

void borrarTabla(Tabla* tabla){
	//Cierro la tabla
	sem_close(tabla->_mutex);
	//Deslinkeo a la tabla del sistema operativo
  	sem_unlink(tabla->_sem_name);
  	//Libero la memoria utilizada para contener el string con el nombre de la tabla 
  	free(tabla->_sem_name);
  	//Libero la memoria compartida de la tabla
  	shmdt((PCB *)tabla->_data);
  	shmctl(tabla->_shmem, IPC_RMID, 0);
}

void _lockTabla(Tabla* tabla){
	sem_wait(tabla->_mutex);
}

void _unlockTabla(Tabla* tabla){
	sem_post(tabla->_mutex);
}

int _getElementosActuales(Tabla * tabla) {
	int cantElem = 0;
	int i = 0;
	while(i < tabla->_cantMaxElementos) {
		if(tabla->_data[i].pid != 0) {
			cantElem++;
		}
		i++;
	}
	return cantElem; 
}

int _getNewPid(Tabla * tabla) {
	int i=0;
	int maxPid = 0;
	while(i < tabla->_cantMaxElementos) {
		if(tabla->_data[i].pid > maxPid) {
			maxPid = tabla->_data[i].pid;
		}
		i++;
	}
	return maxPid + 1;
}
int insertarProceso(Tabla* tabla, const char * path){
	int i = 0;
	
	_lockTabla(tabla);
	int cantElementosActuales = _getElementosActuales(tabla);
	//Verifico que la lista no este llena
	if (cantElementosActuales < tabla->_cantMaxElementos){
		//Los elementos disponibles son los que tienen pid = 0
		while(tabla->_data[i].pid != 0 && i < tabla->_cantMaxElementos ) {
			i++;
		}
		strcpy((char*)tabla->_data[i].name,path);
		tabla->_data[i].state = LTT;
		tabla->_data[i].inMemory = 0;
		tabla->_data[i].timestamp = time(NULL);
		tabla->_data[i].pid = _getNewPid(tabla);
		_unlockTabla(tabla);
		return tabla->_data[i].pid;
	}else{
		_unlockTabla(tabla);
		return RET_ERROR;
	}
} 

int cambiarEstado(Tabla * tabla, int pid, int state) {
	int i = 0;
	
	_lockTabla(tabla);
	while(tabla->_data[i].pid != pid && i < tabla->_cantMaxElementos) {
		i++;
	}
	if(tabla->_data[i].pid == pid) {
		tabla->_data[i].state = state;
		_unlockTabla(tabla);
		return RET_OK;
	}
	else {
		_unlockTabla(tabla);
		return RET_ERROR;
	}
}

int borrarProceso(Tabla * tabla, int pid) {
	int i = 0;
	
	_lockTabla(tabla);
	while(tabla->_data[i].pid != pid && i < tabla->_cantMaxElementos) {
		i++;
	}
	if(tabla->_data[i].pid == pid) {
		//El elemento queda disponible con pid = 0
		tabla->_data[i].pid = 0;
		free(tabla->_data[i].proc);
		//tabla->_cantElementosActuales--;
		_unlockTabla(tabla);
		return RET_OK;
	}
	else {
		_unlockTabla(tabla);
		return RET_ERROR;
	}
	
}

int getState(Tabla * tabla, int pid) {
	int i = 0;
	
	_lockTabla(tabla);
	while(tabla->_data[i].pid != pid && i < tabla->_cantMaxElementos) {
		i++;
	}
	if(tabla->_data[i].pid == pid) {
		_unlockTabla(tabla);
		return tabla->_data[i].state;
	}
	else {
		_unlockTabla(tabla);
		return RET_ERROR;
	}
}

char * getName(Tabla * tabla, int pid) {
	int i = 0;
	_lockTabla(tabla);
	
	while(tabla->_data[i].pid != pid && i < tabla->_cantMaxElementos) {
		i++;
	}
	if(tabla->_data[i].pid == pid) {
		_unlockTabla(tabla);
		return tabla->_data[i].name;
	}
	else {
		_unlockTabla(tabla);
		return NULL;
	}
}

int enMemoria(Tabla * tabla, int pid) {
	int i = 0;
	
	_lockTabla(tabla);
	while(tabla->_data[i].pid != pid && i < tabla->_cantMaxElementos) {
		i++;
	}
	if(tabla->_data[i].pid == pid) {
		_unlockTabla(tabla);
		return tabla->_data[i].inMemory;
	}
	else {
		_unlockTabla(tabla);
		return RET_ERROR;
	}
}

int cambiarEstadoMemoria(Tabla * tabla, int pid, int inMemory) {
	int i = 0;
	
	_lockTabla(tabla);
	while(tabla->_data[i].pid != pid && i < tabla->_cantMaxElementos) {
		i++;
	}
	if(tabla->_data[i].pid == pid) {
		tabla->_data[i].inMemory = inMemory;
		_unlockTabla(tabla);
		return RET_OK;
	}
	else {
		_unlockTabla(tabla);
		return RET_ERROR;
	}
}

void setProcesoCodigo(Tabla * tabla, int pid, Proceso * p) {
	int i = 0;
	
	_lockTabla(tabla);
	while(tabla->_data[i].pid != pid && i < tabla->_cantMaxElementos) {
		i++;
	}
	if(tabla->_data[i].pid == pid) {
		tabla->_data[i].proc = p;
		tabla->_data[i].inMemory = 1;
	}
	_unlockTabla(tabla);
}

void liberarProcesoCodigo(Tabla * tabla, int pid) {
	int i = 0;
	
	_lockTabla(tabla);
	while(tabla->_data[i].pid != pid && i < tabla->_cantMaxElementos) {
		i++;
	}
	if(tabla->_data[i].pid == pid && tabla->_data[i].inMemory) {
		free(tabla->_data[i].proc);
	}
	_unlockTabla(tabla);
}

Proceso * getProceso(Tabla * tabla, int pid) {
	int i = 0;
	
	_lockTabla(tabla);
	while(tabla->_data[i].pid != pid && i < tabla->_cantMaxElementos) {
		i++;
	}
	if(tabla->_data[i].pid == pid && tabla->_data[i].inMemory) {
		_unlockTabla(tabla);
		return tabla->_data[i].proc;
	}
	else {
		_unlockTabla(tabla);
		return NULL;
	}
}

int suspendido(Tabla * tabla, int pid, char * pathImagen) {
	int i = 0;
	char * fileName;
	char * pathCompleto = (char *)reservar(sizeof(char)*300);
	
	_lockTabla(tabla);
	while(tabla->_data[i].pid != pid && i < tabla->_cantMaxElementos) {
		i++;
	}
	fileName = getFileName(tabla->_data[i].name);
	sprintf(pathCompleto,"%s%s.imagen", pathImagen,fileName);

	if(tabla->_data[i].pid == pid) {
		if(_existeArchivo(pathCompleto)) {
			_unlockTabla(tabla);
			return 1;
		}
	}
	_unlockTabla(tabla);
	return 0;
}

int _existeArchivo(const char * filename)
{	FILE * file;
    if ((file = fopen(filename, "r")))
    {
        fclose(file);
        return 1;
    }
    return 0;
}


time_t getTimeStamp(Tabla * tabla, int pid) {
	int i = 0;
	_lockTabla(tabla);
	time_t t;
	
	while(tabla->_data[i].pid != pid && i < tabla->_cantMaxElementos) {
		i++;
	}
	if(tabla->_data[i].pid == pid) {
		_unlockTabla(tabla);
		t = tabla->_data[i].timestamp;
	}
	else {
		_unlockTabla(tabla);
	}
	return t;
}

void resetTimeStamp(Tabla * tabla, int pid) {
	int i = 0;
	_lockTabla(tabla);
	time_t t = time(NULL);
	
	while(tabla->_data[i].pid != pid && i < tabla->_cantMaxElementos) {
		i++;
	}
	if(tabla->_data[i].pid == pid) {
		tabla->_data[i].timestamp = t;
		
	}
	_unlockTabla(tabla);
	return;
}
